home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
Palettes
/
Calculator
/
Calculator.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
19KB
|
1,038 lines
//
// Calculator.m
// Copyright (c) 1990, 1991,1992 by Jiro Nakamura
// All rights reserved
//
// Maintains a simple 4++ function calculator in Objective-C
// Doesn't handle algebraic notation or simple ordering.
//
// by Jiro Nakamura (jiro@shaman.com)
//
// RCS Information
// Revision Number-> $Revision: 1.8 $
// Last Revised-> $Date: 92/02/02 18:24:36 $
//
static char rcsid[] = "$Id: Calculator.m,v 1.8 92/02/02 18:24:36 jiro Exp Locker: jiro $";
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/Matrix.h>
#import <appkit/Panel.h>
#import <appkit/Pasteboard.h>
#import <appkit/publicWraps.h> /* for NXBeep( ) */
#import <appkit/ScrollView.h>
#import "appkit/TextField.h"
#import <strings.h>
#import "Calculator.h"
#import <math.h>
#import <signal.h>
#import <libc.h>
#define OP_NOOP 0
#define OP_ADD 1
#define OP_SUBTRACT 2
#define OP_MULTIPLY 3
#define OP_DIVIDE 4
#define OP_LOGICAL_OR 5
#define OP_LOGICAL_AND 6
#define OP_LOGICAL_EOR 7
#define OP_LOGICAL_NOR 8
#define OP_POWER 9
#define SHIFTMASK (NX_ALTERNATEMASK | NX_SHIFTMASK)
char *OperatorDesc[10] =
{
"=",
"+",
"-",
"x",
"/",
"|",
"&",
"eor",
"nor",
"^"};
#define CALCULATOR_ICON "window.calculator.tiff"
#define CALCULATOR_TITLE_SHORT "T.I. 68040"
#define CALCULATOR_TITLE_LONG "Tennessee Instruments"
#define DM_BINARY 2
#define DM_OCTAL 8
#define DM_DECIMAL 10
#define DM_HEXADECIMAL 16
// The minimum dimensions of the window
#define MIN_WIDTH 210.0
#define MIN_HEIGHT 350.0
// Together, these two declarations form the floating point exception
// part of Calculator
id me; // Class global attachment to self set in +new
BOOL fpError; // Global floating point error indicator
void floatingError(int sig)
{
[me errorDisplay: "- Floating point error"];
fpError = YES;
return;
}
// Math operators
double magic( double y, int op, double x)
{
switch( op)
{
case OP_ADD:
return y + x;
case OP_SUBTRACT:
return y - x ;
case OP_MULTIPLY:
return y * x;
case OP_DIVIDE:
if( x == 0.0)
fpError = YES;
else
return y / x;
case OP_LOGICAL_OR:
return (int) y | (int) x;
case OP_LOGICAL_AND:
return (int) y & (int) x;
case OP_LOGICAL_EOR:
return (int) y ^ (int) x;
case OP_POWER:
return pow( y, x);
case OP_NOOP:
return y;
default:
NXRunAlertPanel("Unrecognizable math.",
"Contact Jiro: jiro@shaman.com",
"OK",NULL, NULL);
}
return 0.0;
}
// Not the most efficient (timewise) but....
// Note: x should be an integer.
double factorial(double x)
{
double y;
if( x <= 1.0)
return 1.0;
if( x >= 170) // Larger than this overflows a double
{
[me errorDisplay: "- Overflow"];
return -1;
}
y = rint(x);
x = 1;
while( y )
x *= y--;
return x;
}
int readBinary( const char *buf )
{
int tmp = 0;
const char *pt = buf;
for( ; *pt != '\0' && (*pt == '0' || *pt == '1'); pt ++ )
tmp = (tmp << 1) + (*pt - '0') ;
return tmp;
}
char * writeBinary(char *buf, int val )
{
int tmp;
for( tmp = 15; tmp >= 0; tmp -- )
{
buf[15 - tmp] = (val & (1 << tmp) ) ? '1' : '0';
}
buf[16] = '\0';
return buf;
}
@implementation Calculator
- initCalc
{
#ifdef DEBUG
fprintf(stderr,"Initing calculator object\n");
#endif
if( calcDidInit == YES)
return self;
m = 0.0;
x = 0.0;
y = 0.0;
x_isNew = YES;
displayMode = DM_DECIMAL;
x_hasDecimal = NO;
currentOperation = OP_NOOP;
[self updateOperationMarker];
fpError = NO;
signal( SIGFPE, floatingError);
me = self;
calcDidInit = YES;
scrollText = [scrollDisplay docView];
[scrollText setAlignment: NX_RIGHTALIGNED];
return self;
}
- key_add:sender
{
[self processPrevious];
currentOperation = OP_ADD;
[self updateOperationMarker];
return self;
}
- key_subtract:sender
{
[self processPrevious];
currentOperation = OP_SUBTRACT;
[self updateOperationMarker];
return self;
}
- key_multiply:sender
{
[self processPrevious];
currentOperation = OP_MULTIPLY;
[self updateOperationMarker];
return self;
}
- key_divide:sender
{
[self processPrevious];
currentOperation = OP_DIVIDE;
[self updateOperationMarker];
return self;
}
- key_logicalOr: sender
{
[self processPrevious];
currentOperation = OP_LOGICAL_OR;
[self updateOperationMarker];
return self;
}
- key_logicalAnd: sender
{
[self processPrevious];
currentOperation = OP_LOGICAL_AND;
[self updateOperationMarker];
return self;
}
- key_logicalEor: sender
{
[self processPrevious];
currentOperation = OP_LOGICAL_EOR;
[self updateOperationMarker];
return self;
}
- key_clear:sender
{
if( fpError)
{
NXBeep();
return self;
}
x = 0;
x_hasDecimal = NO;
x_isNew = YES;
[self setDisplay];
return self;
}
- key_number:sender
{
char key;
static char tmp[80];
// We cannot do this from IB and we do not have a
// appDidInit method
[invisibleEnterKey setKeyEquivalent:3];
if( fpError)
{
NXBeep();
return self;
}
if( x_isNew)
{
[self clearDisplay];
x_isNew = NO;
}
key = *[[sender selectedCell]title];
#ifdef DEBUG
fprintf(stderr,"Key = %c\n", key);
#endif
if( key == '.') // Decimal point
if( x_hasDecimal || displayMode != DM_DECIMAL )
{
NXBeep();
return self;
}
else
x_hasDecimal = YES;
if( key == '0' && *([display stringValue]) == '0'
&& x_hasDecimal == NO && displayMode == DM_DECIMAL)
{
NXBeep();
return self;
}
strcpy( tmp, [display stringValue]);
strcat( tmp, [[sender selectedCell]title]);
[display setStringValue: tmp];
[self getDisplay];
return self;
}
- key_log:sender
{
if( fpError)
{
NXBeep();
return self;
}
[self getDisplay];
if( ([NXApp currentEvent]->flags) & SHIFTMASK)
{
[self setScrollOperation: "Ln" andNumber: x];
x = log( x );
}
else
{
[self setScrollOperation: "Log" andNumber: x];
x = log10( x );
}
[self setDisplay];
[self setScrollOperation: "=" andNumber: x];
return self;
}
- key_squareRoot:sender
{
if( fpError)
{
NXBeep();
return self;
}
[self getDisplay];
// alternate key pressed
if( ([NXApp currentEvent]->flags) & SHIFTMASK)
{
[self setScrollOperation: "square" andNumber: x];
x = pow( x, 2);
}
else
{
[self setScrollOperation: "root" andNumber: x];
if( x >= 0.0)
x = sqrt(x);
else
{
[self errorDisplay: "- Root of negative"];
return self;
}
}
[self setDisplay];
[self setScrollOperation: "=" andNumber: x];
return self;
}
- key_memory:sender
{
if( fpError)
{
NXBeep();
return self;
}
[self getDisplay];
#ifdef DEBUG
fprintf(stderr, "Memory: Tag = %d, m = %f, x = %f\n",
[sender tag], m, x);
#endif
switch( [[sender selectedCell] tag])
{
case 0: // M+
m += x;
[self setScrollOperation: "M+" andNumber: x];
break;
case 1: // M-
m -= x;
[self setScrollOperation: "M-" andNumber: x];
break;
case 2: // M=
m = x;
[self setScrollOperation: "M=" andNumber: x];
break;
case 3: // MR
x = m;
x_isNew = YES;
x_hasDecimal = NO;
[self setDisplay];
[self setScrollOperation: "MR" andNumber: x];
break;
case 4: // MC
m = 0;
break;
default:
NXRunAlertPanel("Dumb Error",
"Key_memory returns false case. Why? "
"Ask Jiro.", "OK", NULL, NULL);
break;
}
[self updateMemoryMarker];
return self;
}
- key_factorial:sender
{
if( fpError)
{
NXBeep();
return self;
}
[self getDisplay];
if( ([NXApp currentEvent]->flags) & SHIFTMASK)
{
[self setScrollOperation: "1/" andNumber: x];
if( x != 0.0)
x = 1/x;
else
{
[self errorDisplay: "- Division by zero"];
return self;
}
}
else
{
[self setScrollOperation: "factorial" andNumber: x];
if( x != rint(x) ) // if x is a non-integer
{
[self errorDisplay: "- Non-integer !"];
return self;
}
if( x < 0.0 ) // if x is negative
{
[self errorDisplay: "- Negative !"];
return self;
}
x = factorial(x);
if( fpError )
{
[self errorDisplay];
return nil;
}
}
[self setDisplay];
[self setScrollOperation: "=" andNumber: x];
return self;
}
- key_enter:sender
{
#ifdef DEBUG
fprintf(stderr, "Enter key hit. y = %f. Op = %d.\n", y,
currentOperation);
#endif
if( fpError)
{
NXBeep();
return self;
}
[self getDisplay];
if( currentOperation != OP_NOOP)
{
[self setScrollOperation: OperatorDesc[currentOperation]
andNumber: x];
x = magic(y, currentOperation, x);
if( fpError )
{
[self errorDisplay];
return nil;
}
currentOperation = OP_NOOP;
[self updateOperationMarker];
}
[self setDisplay];
[self setScrollOperation: "="
andNumber: x];
return self;
}
- key_allClear:sender
{
#ifdef DEBUG
fprintf(stderr,"All Clear\n");
#endif
x = y = 0.0;
x_isNew = YES;
x_hasDecimal = NO;
currentOperation = OP_NOOP;
[self updateOperationMarker];
fpError = NO;
[self setDisplay];
[scrollText setSel: [scrollText textLength]
: [scrollText textLength]];
[scrollText replaceSel: "\nAC\n"];
[scrollText scrollSelToVisible];
[scrollText hideCaret];
[self makeFirstResponder: self];
return self;
}
- key_power:sender
{
if( fpError)
{
NXBeep();
return self;
}
// alternate key pressed
if( ([NXApp currentEvent]->flags) & SHIFTMASK)
{
[self setScrollOperation: "exp" andNumber: x];
[self getDisplay];
x = exp(x);
[self setDisplay];
[self setScrollOperation: "=" andNumber: x];
}
else
{
[self processPrevious];
currentOperation = OP_POWER;
[self updateOperationMarker];
}
return self;
}
- key_negate:sender
{
if( fpError || displayMode == DM_OCTAL || displayMode == DM_BINARY)
{
NXBeep();
return self;
}
[self getDisplay];
if( ([NXApp currentEvent]->flags) & SHIFTMASK)
{
[self setScrollOperation: "abs" andNumber: x];
x = fabs(x);
}
else
{
[self setScrollOperation: "+/-" andNumber: x];
x = -x;
}
[self setDisplay];
[self setScrollOperation: "=" andNumber: x];
return self;
}
- key_baseChanged: sender
{
static char buf[30];
switch( atoi( [[sender selectedCell]title]) )
{
case 2: // Previous was base 2, so new is base 8
displayMode = DM_OCTAL;
[hexadecimalKeyMatrix setEnabled: NO];
[[sender selectedCell] setTitle: "8"];
[decimalKeyMatrix setEnabled: YES];
[[decimalKeyMatrix findCellWithTag: 8] setEnabled: NO];
[[decimalKeyMatrix findCellWithTag: 9] setEnabled: NO];
[decimalPointKey setEnabled: NO];
break;
case 8: // Previous was base 8, so new is base 10
displayMode = DM_DECIMAL;
[[sender selectedCell] setTitle: "10"];
[hexadecimalKeyMatrix setEnabled: NO];
[[decimalKeyMatrix findCellWithTag: 8]
setEnabled: YES];
[[decimalKeyMatrix findCellWithTag: 9] setEnabled:
YES];
[decimalPointKey setEnabled: YES];
break;
case 10: // Previous was base 10, so new is base 16
displayMode = DM_HEXADECIMAL;
[hexadecimalKeyMatrix setEnabled: YES];
[[sender selectedCell] setTitle: "16"];
[decimalPointKey setEnabled: NO];
break;
case 16: // Previous was base 16, so new is base 2
displayMode = DM_BINARY;
[hexadecimalKeyMatrix setEnabled: NO];
[[sender selectedCell] setTitle: "2"];
[decimalKeyMatrix setEnabled: NO];
[[decimalKeyMatrix findCellWithTag: 0]
setEnabled: YES];
[[decimalKeyMatrix findCellWithTag: 1]
setEnabled: YES];
[decimalPointKey setEnabled: NO];
break;
default:
NXBeep();
return nil;
}
[self setDisplay];
sprintf(buf, "\nChange to base %d", displayMode );
[scrollText setSel: [scrollText textLength]
: [scrollText textLength]];
[scrollText replaceSel: buf];
[self setScrollOperation: "=" andNumber: x];
[self getDisplay];
return self;
}
- setDisplay
{
static char displayBuf[20];
#ifdef DEBUG
fprintf(stderr,"Display is set to %f.\n", x);
#endif
switch (displayMode )
{
case DM_BINARY:
writeBinary(displayBuf, x);
[display setStringValue: displayBuf];
break;
case DM_OCTAL:
sprintf( displayBuf, "%o", (unsigned int) x);
[display setStringValue: displayBuf];
break;
case DM_HEXADECIMAL:
sprintf( displayBuf, "%x", (unsigned int) x);
[display setStringValue: displayBuf];
break;
case DM_DECIMAL:
default:
[display setDoubleValue: x];
break;
}
x_isNew = YES;
[self updateMemoryMarker];
[self makeFirstResponder: self];
return self;
}
- clearDisplay
{
[display setStringValue: ""];
x_hasDecimal = NO;
x_isNew = YES;
return self;
}
- errorDisplay
{
[display setStringValue: "Error"];
fpError = YES;
return self;
}
- errorDisplay: (char *) errorString
{
static char buf[80];
fpError = YES;
sprintf(buf, "Error %s", errorString);
[display setStringValue: buf];
sprintf(buf, "\nError %s", errorString);
[scrollText setSel: [scrollText textLength]
: [scrollText textLength]];
[scrollText replaceSel: buf];
[scrollText scrollSelToVisible];
[scrollText hideCaret];
[self makeFirstResponder: self];
return self;
}
- (double) getDisplay
{
unsigned int utmp;
if( !calcDidInit )
return( x = 0.0);
switch (displayMode)
{
case DM_BINARY:
x = readBinary([display stringValue] );
break;
case DM_OCTAL:
sscanf([display stringValue], "%o", &utmp);
x = utmp;
break;
case DM_HEXADECIMAL:
sscanf([display stringValue], "%x", &utmp);
x = utmp;
break;
case DM_DECIMAL:
default:
x = [display doubleValue];
break;
}
return( x );
}
- updateMemoryMarker
{
if( m == 0.0 )
[memoryDisplay setStringValue: ""];
else
[memoryDisplay setStringValue: "M"];
return self;
}
- updateOperationMarker
{
const char* operation;
switch( currentOperation )
{
case OP_ADD:
operation = "+";
break;
case OP_SUBTRACT:
operation = "-";
break;
case OP_MULTIPLY:
operation = "x";
break;
case OP_DIVIDE:
operation = "/";
break;
case OP_LOGICAL_OR:
operation = "OR";
break;
case OP_LOGICAL_AND:
operation = "AND";
break;
case OP_LOGICAL_EOR:
operation = "EOR";
break;
case OP_LOGICAL_NOR:
operation = "NOR";
break;
case OP_NOOP:
default:
operation = " ";
break;
}
[operationDisplay setStringValue: operation];
return self;
}
// Delegate stuff to ensure proper miniaturization behaviour
- windowWillMiniaturize: sender
toMiniwindow: mini
{
[self setTitle: CALCULATOR_TITLE_SHORT];
return self;
}
- windowDidUpdate: sender
{
if( calcDidInit != YES)
[self initCalc];
[self setMiniwindowIcon: CALCULATOR_ICON];
[self makeFirstResponder: self];
return self;
}
- windowDidDeminiaturize: sender
{
[self setTitle: CALCULATOR_TITLE_LONG];
[self makeFirstResponder: self];
return self;
}
- processPrevious
{
if( fpError)
{
NXBeep();
return self;
}
if( currentOperation != OP_NOOP)
{
[self getDisplay];
[self setScrollOperation: OperatorDesc[currentOperation]
andNumber: x];
y = magic(y, currentOperation, x);
if( fpError )
{
[self errorDisplay];
return nil;
}
x = y;
[self setDisplay];
x_isNew = YES;
}
else
{
[self getDisplay];
y = x;
x_isNew = YES;
}
[self setScrollOperation: "=" andNumber: x];
return self;
}
- copy: sender
{
static char pasteBuffer[80];
const char *types[] = {NXAsciiPboardType,NULL};
id pasteBoard;
if( fpError)
{
NXBeep();
return self;
}
strcpy( pasteBuffer, [display stringValue]);
pasteBoard = [Pasteboard newName: NXGeneralPboard];
[pasteBoard declareTypes: types
num: 1
owner: NULL];
[pasteBoard writeType: NXAsciiPboardType
data: pasteBuffer
length: (int) strlen(pasteBuffer)];
#ifdef DEBUG
fprintf(stderr, "Copied <%s> to the Pasteboard.\n",
pasteBuffer);
#endif
fprintf(stderr, "Copy!\n");
return self;
}
- paste: sender
{
static char *bufPt;
int len;
int tmp;
const NXAtom *types;
id pasteBoard;
if( fpError)
{
NXBeep();
return self;
}
pasteBoard = [Pasteboard newName: NXGeneralPboard];
types = [pasteBoard types];
for( tmp = 0; types[tmp] != NULL; tmp ++ )
if( strcmp( types[tmp], NXAsciiPboardType ) == 0 )
{
[pasteBoard readType: NXAsciiPboardType
data: &bufPt
length: &len];
[display setStringValue: bufPt];
[self getDisplay];
[self setDisplay];
[self setScrollOperation: "Paste: " andNumber: x];
#ifdef DEBUG
fprintf(stderr, "Pasted <%s> (%d) from "
"the Pasteboard.\n", bufPt,
len);
#endif
break;
}
return self;
}
- setScrollOperation: (char *) op andNumber: (double) val
{
static char buffer[30], buf2[30];
switch (displayMode )
{
case DM_BINARY:
writeBinary( buf2, val);
sprintf(buffer, "\n%s %s", op, buf2);
break;
case DM_OCTAL:
sprintf( buffer, "\n%s %o",
op, (unsigned int) val);
break;
case DM_HEXADECIMAL:
sprintf( buffer, "\n%s %x",
op, (unsigned int) val);
break;
case DM_DECIMAL:
default:
sprintf( buffer, "\n%s %g",
op, val);
break;
}
[scrollText setSel: [scrollText textLength]
: [scrollText textLength]];
[scrollText replaceSel: buffer];
[scrollText hideCaret];
[scrollText scrollSelToVisible];
[self makeFirstResponder: self];
return self;
}
- windowWillResize: (id) sender toSize: (NXSize *) size
{
#ifdef DEBUG
fprintf(stderr,"Window would have resized to %f x %f.\n",
size->width, size->height);
#endif
if( size->width < MIN_WIDTH)
size->width = MIN_WIDTH;
if( size->height < MIN_HEIGHT)
size->height = MIN_HEIGHT;
#ifdef DEBUG
fprintf(stderr,"Window will resize to %f x %f.\n",
size->width, size->height);
#endif
return self;
}
- (BOOL) acceptsFirstResponder { return YES; }
- printPSCode: sender { [scrollText printPSCode: sender]; return self;}
// From CalculatorLab/MinusPanel.m
// When the user presses the minus sign on the keypad, the resulting character
// is number 45 from the Symbol character set, not a minus sign from the ASCII
// character set (which you would get by pressing the key to the right of the
// zero key). The Panel class ignores commandKeys from all sets except the
// ASCII set, but we want the user to be able to depress the minus key on the
// keypad and have it act like the real minus key. We can get around this
// problem by checking for this special minus sign and converting it into an
// ASCII minus sign before it reaches the regular commandKey: method
- (BOOL)commandKey:(NXEvent *)theEvent
{
NXEvent localEvent;
BOOL symbolSet, minusSign;
symbolSet = theEvent->data.key.charSet == NX_SYMBOLSET;
minusSign = theEvent->data.key.charCode == 45;
if (symbolSet && minusSign) { /* check for minus */
localEvent = *theEvent;
localEvent.data.key.charSet = NX_ASCIISET;
localEvent.data.key.charCode = '-';
return [super commandKey:&localEvent];
} else {
return [super commandKey:theEvent];
}
}
@end